/**************************************************************
          File Name:thread_c11.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: 2015 03 31 19:32:22
***************************************************************/
/* in freebsd 10.1: clang threads.c -lstdthreads */
#include<threads.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>

char *file_in = NULL;
char *file_out = NULL;

static void err(const char *s){
    perror(s);
    exit(errno);
}

typedef struct{
    long block;
    long start;
} pkg;

mtx_t mtx;

int pwreadn(int fdin, int fdout, long offt, long size)
{
    long len = size;
    long res = 0;
    char *buf = (char *)malloc(size);

    while(len)
    {
        res = read(fdin, buf, len);

        if(res == -1)
            return -1;
        if(res == 0)
            break;
        len -= res;

        if(pwrite(fdout, buf, res, offt) == -1)
            return -1;
    }
    return 0;
}

int pfun(void *tmp)
{
    pkg *pc = (pkg *)tmp;

    int fdin, fdout;
    int res = 0;
    fdin = open(file_in, O_RDWR);
    fdout = open(file_out, O_CREAT|O_RDWR, 00644);

    if(fdin == -1 || fdout == -1)
        err("open");
    lseek(fdin, pc->start, SEEK_SET);

    printf("start = %ld\t block = %ld\n", pc->start, pc->block);
    mtx_lock(&mtx);

    printf("thread: %p\n", thrd_current());

    res = pwreadn(fdin, fdout, pc->start, pc->block);
    if(res == -1)
        printf("thread: %p --> rd-wr failed\n", thrd_current());
    close(fdin);
    close(fdout);

    mtx_unlock(&mtx);
    thrd_exit(res);
}

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        fprintf(stderr, "%s srcfile dstfile\n", argv[0]);
        exit(1);
    }
    struct stat st;
    pkg *rpm[4];
    long size,tmp,tmp1;
    int i, res;
    thrd_t tid[4];

    for(i=0; i<4; i++){
        rpm[i] = (pkg *)malloc(sizeof(pkg));
        rpm[i]->start = 0;
        rpm[i]->block = 0;
    }

    stat(argv[1], &st);

    size = st.st_size;
    tmp = size / 4;
    tmp1  = size % 4;

    file_in = argv[1];
    file_out = argv[2];

    for(i = 0; i< 4; i++)
    {
        rpm[i]->block = i == 0 ? tmp + tmp1 : tmp;
        rpm[i]->start = i == 0 ? 0 : (i == 1 ? tmp + tmp1 : rpm[i-1]->start + tmp);
        if(thrd_create(&tid[i], &pfun, (void *)rpm[i]) != thrd_success)
            break;
    }

    for(i = 0; i< 4; i++)
        if(thrd_join(tid[i], &res) != thrd_success)
            fprintf(stderr, "falied to join\n");

    for(i=0; i<4; i++)
        free(rpm[i]);
	
    return errno;
}
